#include "camera.h"
#include "matrix.h"
#include "vector3.h"
#include "algebra.h"

using namespace krryn::system;
using namespace krryn::math;
using namespace krryn::view;

camera_fps::camera_fps(
	input *a_Input, const vector3f &a_Up, 
	const vector3f &a_Target, const vector3f &a_Pos)
: camera(a_Up, a_Target, a_Pos), m_Input(a_Input) {}

// negative speed moves backwards
void camera_fps::move(float a_Speed){
	math::vector3f l_Direction = m_Target - m_Position;

	m_Position.m_X += l_Direction.m_X * a_Speed;
	m_Position.m_Z += l_Direction.m_Z * a_Speed;

	m_Target.m_X += l_Direction.m_X * a_Speed;
	m_Target.m_Z += l_Direction.m_Z * a_Speed;
}

void camera_fps::strafe(float a_Speed){
	math::vector3f l_Direction = m_Target - m_Position;

	m_Position.m_X -= l_Direction.m_Z * a_Speed;
	m_Position.m_Z += l_Direction.m_X * a_Speed;

	m_Target.m_X -= l_Direction.m_Z * a_Speed;
	m_Target.m_Z += l_Direction.m_X * a_Speed;
}

void camera_fps::rotate(float a_Speed){
	math::vector3f l_Direction = m_Target - m_Position;

	m_Target.m_Z = m_Position.m_Z + sin(a_Speed) * l_Direction.m_X
		+ cos(a_Speed) * l_Direction.m_Z;

	m_Target.m_X = m_Position.m_X + cos(a_Speed) * l_Direction.m_X
		- sin(a_Speed) * l_Direction.m_Z;
}

void camera_fps::update(float a_DeltaTime){
	float l_Speed = 40.f * a_DeltaTime;
	float l_RotationSpeed = degree_to_radian(30.f * a_DeltaTime);

	if(m_Input->key_down(key_w) || m_Input->key_down(key_up)){
			move(l_Speed);
	}

	if(m_Input->key_down(key_s) || m_Input->key_down(key_down)){
			move(-l_Speed);
	}

	if(m_Input->key_down(key_a)){
		strafe(-l_Speed);
	}

	if(m_Input->key_down(key_d)){
		strafe(l_Speed);
	}

	if(m_Input->key_down(key_left)){
		rotate(-l_RotationSpeed);
	}

	if(m_Input->key_down(key_right)){
		rotate(l_RotationSpeed);
	}
}

void camera_fps::update_mouse(int a_SceenWidth, int a_SceenHeight){
	vector2<int> l_Position = m_Input->get_mouse_pos_abs();

	// reset the mouse so we get relative coordinates, in pixels
	m_Input->set_mouse_pos_abs(a_SceenWidth / 2, a_SceenHeight / 2);

	float l_MouseSpeed = 500;
	float l_MaxDifference = 8;

	float l_AngleY = ((a_SceenWidth / 2) - l_Position.m_X) / l_MouseSpeed;
	float l_AngleZ = ((a_SceenHeight / 2) - l_Position.m_Y) / l_MouseSpeed;

	m_Target.m_Y += l_AngleZ * 2;

	if(m_Target.m_Y - m_Position.m_Y > l_MaxDifference)
		m_Target.m_Y = m_Position.m_Y + l_MaxDifference;

	if(m_Target.m_Y - m_Position.m_Y < -l_MaxDifference)
		m_Target.m_Y = m_Position.m_Y - l_MaxDifference;

	rotate(-l_AngleY);
}

matrix camera_fps::get_look_at_matrix(){
	const math::vector3f l_AxisZ = (m_Target - m_Position).normalize();
	const math::vector3f l_AxisX = m_Up.cross(l_AxisZ);
	const math::vector3f l_AxisY = l_AxisX.cross(l_AxisZ);

	matrix l_LookAtMatrix;

	l_LookAtMatrix[0] = l_AxisX.m_X;
	l_LookAtMatrix[1] = l_AxisX.m_Y;
	l_LookAtMatrix[2] = l_AxisX.m_Z;

	l_LookAtMatrix[4] = l_AxisY.m_X;
	l_LookAtMatrix[5] = l_AxisY.m_Y;
	l_LookAtMatrix[6] = l_AxisY.m_Z;

	l_LookAtMatrix[8] = l_AxisZ.m_X;
	l_LookAtMatrix[9] = l_AxisZ.m_Y;
	l_LookAtMatrix[10] = l_AxisZ.m_Z;

	l_LookAtMatrix.invert();

	l_LookAtMatrix[3] = m_Position.m_X;
	l_LookAtMatrix[7] = m_Position.m_Y;
	l_LookAtMatrix[11] = m_Position.m_Z;

	return l_LookAtMatrix;
}
